<!DOCTYPE html>
          <head>
        <meta charset="utf-8">
            
            <title>
                「Android」0x03本地数据安全尝试JNI | ttdevs
            </title>
            <meta content="width=device-width, initial-scale=1" name="viewport">
            <meta name="theme-color" content="#4184f3">
            
            
            <link href="/favicon.ico" rel="icon"/>
            

            <link rel="stylesheet" href="/css/highlight.light.css">
            <link rel="stylesheet" href="/css/prism-customize.css">
            <link rel="stylesheet" href="/css/nav-icon.css">
            <link rel="stylesheet" href="/css/waves.min.css">
            <link rel="stylesheet" href="/css/jquery.tocify.css">
            <link rel="stylesheet" href="/css/main.css">
            <link rel="stylesheet" href="/css/nav-indicator.css">
            
  

  
  <!-- 谷歌统计 -->
  <script>
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    ga('create', 'UA-97465173-1', 'auto');
    ga('send', 'pageview');

  </script>
  
            </meta>
        </meta>
    </head>

    <body>
        <header>
            <!-- cover image or sth. -->
        </header>
        <div id="main" class="m-scene">
            
<div class="nav-wrapper">

    <div class="container">
        <nav>
            <div class="logo wave">
                <a href="/" id="logo">
                    ttdevs
                </a>
            </div>
            <div class="nav-toggle-icon" >
                <div class="material-hamburger">
                    <span>
                    </span>
                    <span>
                    </span>
                    <span>
                    </span>
                </div>
            </div>
            <div class="menu-wrapper">
                <div class="nav-indicator">
                </div>
                <ul class="menus">
                    
                     
                        <li>
                            <a class="wave " href="/">
                                首页
                            </a>
                        </li>
                     
                        <li>
                            <a class="wave " href="/archives">
                                归档
                            </a>
                        </li>
                     
                        <li>
                            <a class="wave " href="/about">
                                关于
                            </a>
                        </li>
                     
                    
                   
                </ul>
            </div>
        </nav>
    </div>
</div>
            <div class="container content">
                <div class="scene_element scene_element--fadein">
                    <div class="row">
    <div class="main">
        <article>
          
          <header class="post-header with-cover" style="background-image:url('/1970/01/01/[Android]0x03本地数据安全尝试JNI/cover.jpg')" >
          
          </header>
          <h1 class="post-title">「Android」0x03本地数据安全尝试JNI</h1>

          <section class="post-info">
            <span class="post-date">1970/01/01</span>
            
            <span class="post-category">
                <a class="article-category-link" href="/categories/技术/">技术</a>
            </span>
            
            
            <span class="post-tags">
              <ul class="post-tag-list"><li class="post-tag-list-item"><a class="post-tag-list-link" href="/tags/Android/">Android</a></li></ul>
            </span>
            
          </section>

          <section class="post-content">
            <p>前两篇，我们谈到了使用SQLCipher和Conceal对本地数据进行加密。由于都两种方法都采用了对称加密，因此我们需要自己管理加密的秘钥。这时你会发现，虽然对我们的数据进行了加密，但是我们却引入了新的问题。我们的加密方法很容易通过反编译apk获取到，那么，我们就需要安全的维护这个秘钥了。但遗憾的是，本地数据存储方式我们都已经讲述，并没有一种一劳永逸的安全保存方法，那么，我们的秘钥存在哪里合适呢？这个时候你可能会想到本地不行那我们存到服务器上吧，通过https进行传输。这样当然可以，通过一定的算法为每个人配置一个秘钥，需要的时候请求网络获取，然后对本地数据进行解密。但是这样也存在一个问题：本地保存的数据如果不联网就无法打开。那么，还有更好的方案吗？今天给大家介绍JNI。</p>
<h2 id="0x01"><a href="/1970/01/01/[Android]0x03本地数据安全尝试JNI/#0x01" class="headerlink" title="0x01"></a>0x01</h2><p>NDK，JNI对于刚接触android开发的攻城狮来说是有较大的门槛的，但是为了实现我们更安全的保存数据，他可能是不错的选择。反编译过别人家app的你可能都遇到过这样的情况：一个个没无法查看代码逻辑的so文件。这些文件我们可以使用java代码调用，实现一些我们不知道内部逻辑但是会给我们一个结果功能。那么，我们把密码放到这些so中就可以更进一步提高我们本地数据的安全级别。</p>
<p>下面通过一个加单的demo来看看JNI的实现。</p>
<p>首先创建一个工具类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CipherUtil</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">static</span> &#123;</div><div class="line">        System.loadLibrary(<span class="string">"Cipher"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">native</span> String <span class="title">getCipherKey</span><span class="params">()</span></span>;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>在这个类文件上点击右键，使用我们之前配置的javah工具生成头文件。之后我们会在和java目录同级的jni文件下看到一个.h文件，如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/* DO NOT EDIT THIS FILE - it is machine generated */</span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;jni.h&gt;</span></span></div><div class="line"><span class="comment">/* Header for class com_ttdevs_ndk_CipherUtil */</span></div><div class="line"></div><div class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> _Included_com_ttdevs_ndk_CipherUtil</span></div><div class="line"><span class="meta">#<span class="meta-keyword">define</span> _Included_com_ttdevs_ndk_CipherUtil</span></div><div class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> __cplusplus</span></div><div class="line"><span class="keyword">extern</span> <span class="string">"C"</span> &#123;</div><div class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></div><div class="line"><span class="comment">/*</span></div><div class="line"> * Class:     com_ttdevs_ndk_CipherUtil</div><div class="line"> * Method:    getCipherKey</div><div class="line"> * Signature: ()Ljava/lang/String;</div><div class="line"> */</div><div class="line"><span class="function">JNIEXPORT jstring JNICALL <span class="title">Java_com_ttdevs_ndk_CipherUtil_getCipherKey</span></span></div><div class="line">  <span class="params">(JNIEnv *, jclass)</span>;</div><div class="line"></div><div class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> __cplusplus</span></div><div class="line">&#125;</div><div class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></div></pre></td></tr></table></figure>
<p>然后在h文件的同级新建一个C++文件（右键&gt;New&gt;C/C++ Source file）,内容如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"QiniuConfig.h"</span></span></div><div class="line"></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;android/log.h&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">define</span> LOG_TAG <span class="meta-string">"System.out"</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)</span></div><div class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)</span></div><div class="line"></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"com_ttdevs_ndk_CipherUtil.h"</span></span></div><div class="line"></div><div class="line"><span class="function">JNIEXPORT jstring JNICALL <span class="title">Java_com_ttdevs_ndk_CipherUtil_getCipherKey</span><span class="params">(JNIEnv *env, jclass)</span></span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">return</span> (*env).NewStringUTF(<span class="string">"Hello World! getCipherKey"</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>我们还需要创建两个文件，一个叫Android.mk，另一个叫Application.mk，他们都在jni目录下。</p>
<ul>
<li>Android.mk:</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"># http://developer.android.com/intl/zh-tw/ndk/guides/android_mk.html</div><div class="line"></div><div class="line">LOCAL_PATH := $(call my-dir)</div><div class="line"></div><div class="line">include $(CLEAR_VARS)</div><div class="line"></div><div class="line">LOCAL_MODULE := Cipher</div><div class="line">LOCAL_SRC_FILES := Cipher.cpp</div><div class="line">LOCAL_LDLIBS += -llog</div><div class="line"></div><div class="line">include $(BUILD_SHARED_LIBRARY)</div></pre></td></tr></table></figure>
<ul>
<li>Application.mk</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"># http://developer.android.com/intl/zh-tw/ndk/guides/application_mk.html</div><div class="line"></div><div class="line"># APP_STL := stlport_static</div><div class="line">APP_STL := gnustl_static</div><div class="line">APP_CPPFLAGS := -frtti -std=c++11</div><div class="line">APP_CFLAGS += -Wno-error=format-security</div><div class="line"></div><div class="line">APP_ABI := all</div></pre></td></tr></table></figure>
<p>还没有完，我们还需要修改当前Project或者Module的gradle.build文件:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">android &#123;</div><div class="line">    compileSdkVersion 23</div><div class="line">    buildToolsVersion &quot;23.0.2&quot;</div><div class="line"></div><div class="line">    defaultConfig &#123;</div><div class="line">        minSdkVersion 16</div><div class="line">        targetSdkVersion 23</div><div class="line">        versionCode 1</div><div class="line">        versionName &quot;1.0&quot;</div><div class="line"></div><div class="line">        ndk &#123;</div><div class="line">            moduleName &quot;ndkutil&quot;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    sourceSets.main &#123;</div><div class="line">        jni.srcDirs = []</div><div class="line">        jniLibs.srcDir &quot;libs&quot;</div><div class="line">    &#125;</div><div class="line">...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>完成这些，我们就可以进行编译了。在当前Project（Module）上点击右键，使用之前配置的ndk-build工具进行编译，如果没有问题，我们会在libs目录下看到生产的so文件。好了，最后我们可以编写测试代码了：在java直接调用刚才创建的CipherUtil即可：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Log.d(<span class="string">"&gt;&gt;&gt;&gt;&gt;"</span>, CipherUtil.getCipherKey());</div></pre></td></tr></table></figure>
<p>运行上面代码，我们可以在log中看到输出的字符串：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Hello World! getCipherKey</div></pre></td></tr></table></figure>
<h2 id="0x02"><a href="/1970/01/01/[Android]0x03本地数据安全尝试JNI/#0x02" class="headerlink" title="0x02"></a>0x02</h2><p>上述demo中，我们只是简单的返回一个字符串，要实现更安全，我们可以将此方法写的更复杂，比如获取app的签名，获取设备的硬件信息进行复杂的组合，以保障最终生成的秘钥的唯一性和安全性（更难伪造），这里有一个<a href="https://github.com/MasonLiuChn/AndroidUltimateEncrypt" target="_blank" rel="external">demo</a>可以参考。</p>
<p>写到这里，可能又有人会问到：其实so文件也不是很全，可以通过对汇编的分析得到里面的代码逻辑。当然，高手是可以做到对so文件进行分析的，但是so还是可以阻隔大部分的反编译人员。如果我们能把getCipherKey实现的更好，也会增加破解的成本。另外，so还有一个被盗用的问题，就是别人直接调用我们的so，这个问题也可以通过一定的代码逻辑来避免。之后会继续讲解。</p>
<p>最后再说一点，对于秘钥，我们最终还会被载入我们的内存，如果直接dump我们的内存，会是一个人什么样的结果呢？</p>
<hr>
<p><img src="https://raw.githubusercontent.com/ttdevs/ttdevs.github.io/common/images/logo.png" alt="Create by ttdevs"></p>

          </section>
        </article>
        

       
        <div class="pager">
          
            <a class="post-prev pager-item" href="/1970/01/01/[Android]0x02本地数据安全尝试Conceal/" >
              <strong class="article-nav-caption">上一篇</strong>
              <p class="post-nav-title">「Android」0x02本地数据安全尝试Conceal</p>
            </a>
          
          
            <a class="post-next pager-item" href="/1970/01/01/[Android]Android闹钟遇到的那些坑/">
              <strong class="article-nav-caption">下一篇</strong>
              <p class="post-nav-title">「Android」Android闹钟遇到的那些坑</p>
            </a>
          
        </div>
        

         <!-- comments -->
        <div class="comment-section">
  
    


</div>

    </div>
    
    <aside>
        <div id="toc">
        </div>
    </aside>
    
</div>

                </div>
            </div>
        </div>
        <footer class="footer">
    <p>由<a href="http://hexo.io/" target="_blank">Hexo</a>强力驱动，搭载<a href="https://github.com/wayou/hexo-theme-gstyle">gstyle</a>主题</p>
    <p>
        &copy; 2017 ttdevs
    </p>
</footer>
<script src="/lib/jquery.js"></script>
<script src="/lib/waves.js"></script>
<script src="/lib/jquery-ui.js"></script>
<script src="/lib/jquery.tocify.js"></script>
<script src="/js/main.js"></script>

    </body>
</html>
